#ifndef LIBRADARSSD_H
#define LIBRADARSSD_H

#include "dma.h"
#include "command_list.h"

#define MAX_RADAR 16 //最多支持存储多少个已知雷达的参数
#define BINARY_TOA_LENGTH	1000 
#define TOA_READ_COUNT 2048//一次读多少个信号
#define TIME_WINDOW 50 //时间窗口

#define ftype u64
#define FEATURE_COUNT 3
#define PAGE_SIZE 16384
#define FEATURE_SIZE sizeof(ftype)
#define MAX_FEATURE_PERBANK PAGE_SIZE/FEATURE_SIZE
#define BANK_SIZE FEATURE_COUNT*PAGE_SIZE
//多个page组成一个bank，bank的作用是存储一组信号的数据。
//bank中有多少个page取决于特征的数量。一个信号中的各个字段存到一个bank中的不同page中便于集中存储。

#define LAYER1 	1000
#define LAYER2 	128
#define LAYER3 	64
#define LAYER4 	32
#define LAYER5 	4

#define Weight1	(LAYER1*LAYER2)
#define Bias1	(LAYER2)
#define Weight2	(LAYER2*LAYER3)
#define Bias2	(LAYER3)
#define Weight3	(LAYER3*LAYER4)
#define Bias3	(LAYER4)
#define Weight4	(LAYER4*LAYER5)
#define Bias4	(LAYER5)

#define wtype	float

#define MODEL_COUNT	(Weight1+Weight2+Weight3+Weight4+Bias1+Bias2+Bias3+Bias4)
#define MODEL_SIZE 	(sizeof(wtype)*MODEL_COUNT)

struct radar_io_command{
	ftype start_toa;
	ftype end_toa;
	unsigned int data_count; //how many toa signal processing this time
	unsigned int model_count; //how many model does this search use
	unsigned int model_id[MAX_RADAR]; //indicate use which model to search, format:[1,3,7,...]
    u64 timestamp[4];
};

ftype bank[FEATURE_COUNT][MAX_FEATURE_PERBANK]; //主机端bank缓存区
unsigned int bank_queue_used; //标记每个bank还剩余多少空间(以FEATURE_SIZE为单位)，范围为[0,MAX_FEATURE_PERBANK-1]

//For异步写入****************************************
#define BANK_ASYNC  16  //一共设置BANK_ASYNC个bank用于同时写入
//定义BANK写入的状态
#define EMPTY       0   //初始化状态，表示该BANK为空
#define PUSHING     1   //正在由应用写入信号值
#define WRITING     2   //正在由线程进行异步写入

ftype bank_async[BANK_ASYNC][FEATURE_COUNT][MAX_FEATURE_PERBANK];
unsigned bank_status[BANK_ASYNC];   //对应上方的bank状态
static int pushing_bank;                //正在写入哪个bank, -1表示所有bank都忙
static unsigned pushing_bank_offset;    //正在写入bank的偏移量

#define THREAD_COUNT    2 //有多少个线程
static int thread_working_bank[THREAD_COUNT]; //每个线程正在写回哪个bank，-1（初始值）表示没有工作
static pthread_t threads[THREAD_COUNT];       //每个线程的标识符
//*************************************************

//计算盘内返回的时间
u64 spend_time_us(u64 start_time, u64 end_time){
    return ((end_time - start_time) * 1000000) / (500000000);
}

//以下是进行雷达参数设置的API********************************************
void init_radarssd(){
    bank_queue_used = 0;
    //以下是用于异步写入使用
    memset(bank_status,0,BANK_ASYNC*sizeof(unsigned));
    memset(thread_working_bank,-1,THREAD_COUNT*sizeof(int));
    pushing_bank = 0;
    pushing_bank_offset = 0;
}

//用于更新模型参数。虽然模型形状固定，但是可以更新参数以支持不同雷达信号的分选。
//model_id用于标识该模型用于分选哪种类型的信号。与recognize操作中的id相同。
int update_model(wtype* model_parameter, unsigned int parameter_size, int model_id){
    open_unvme();
    DMA_Send(NULL,(char *)model_parameter,parameter_size,RADAR_SET_MODEL,0);
    close_unvme();

    return 0;
}

//更新某个已知雷达的toa列表。model_id用于标识该已知雷达，与model相对应
int update_toa(unsigned int* toa_list, unsigned int parameter_size, int model_id){
    return 0;
}

//以下是进行盘内雷达识别的API********************************************
//在盘内将start_toa后toa_count范围内的雷达信号全部读取出来，并利用之前上传的model和toa列表进行判断是否属于某个信号源。返回包含的信号源id
int radarssd_recognize(ftype start_toa, unsigned int toa_count, int* model_id_list, unsigned int model_count){
    return 0;
}

//以下是进行雷达数据读写的API*********************************************
//异步写入
int push_signal_async(ftype TOA, ftype DOA, ftype RF){
    if(bank_queue_used > MAX_FEATURE_PERBANK-1){ //buffer还有空间，直接放到bank中
        void* result = DMA_Send(NULL,(char *)bank,BANK_SIZE,RADAR_WRITE,bank[0][0]);
        bank_queue_used = 0;
    }
    //此时bank有空间，可以继续写入
    bank[0][bank_queue_used] = TOA;
    bank[1][bank_queue_used] = DOA;
    bank[2][bank_queue_used] = RF;
    bank_queue_used++;

    return 0;
}

//存储一个信号，如果主机队列还有空余则直接插入主机内存并返回，否则会阻塞至落盘结束
int push_signal_noopen(ftype TOA, ftype DOA, ftype RF){
    if(bank_queue_used > MAX_FEATURE_PERBANK-1){ //buffer还有空间，直接放到bank中
        //bank没有空间了
        //发送给cosmos，阻塞等待，cosmos在数据传输结束后即返回
        //printf("BANK Full, flush to radarssd\n");
        //printf("bank range:[%u,%u]\n",bank[0][0],bank[0][MAX_FEATURE_PERBANK-1]);
        void* result = DMA_Send(NULL,(char *)bank,BANK_SIZE,RADAR_WRITE,bank[0][0]);
        //printf("Flush done\n");
        // for(int i=0;i<10;i++)
        //     printf("result[%d]:%u\n",i,result[i]);
        //清空buffer（并没有真正的清空，而是将bank剩余空间重置）
        bank_queue_used = 0;
    }
    //此时bank有空间，可以继续写入
    bank[0][bank_queue_used] = TOA;
    bank[1][bank_queue_used] = DOA;
    bank[2][bank_queue_used] = RF;
    bank_queue_used++;

    return 0;
}

int push_signal(ftype TOA, ftype DOA, ftype RF){
    open_unvme();
    int result = push_signal_noopen(TOA, DOA, RF);
    close_unvme();
    return result;
}

//根据TOA读取一定范围内的TOA信号
unsigned int* read_signal_dma(ftype TOA, unsigned int length){
    struct radar_io_command* command = malloc(sizeof(struct radar_io_command));
    memset((void*)command,0,sizeof(struct radar_io_command));

    command->start_toa = TOA;
    command->data_count = 1500;

    open_unvme();
    void* read_result = DMA_Send(NULL,(char *)command,4096,RADAR_READ,0);
    close_unvme();

    free(command);
    return 0;
}

//根据TOA读取固定范围内的TOA信号
ftype* read_signal_dword(ftype TOA, unsigned int TOA_count){
    open_unvme();
    ftype* toa_list = (ftype*)DMA_Receive(NULL,NULL,TOA_count*FEATURE_SIZE,RADAR_READ,TOA);
    close_unvme();

    return toa_list;
}

ftype* read_signal_dword_unopen(ftype TOA, unsigned int TOA_count){
    ftype* toa_list = (ftype*)DMA_Receive(NULL,NULL,TOA_count*FEATURE_SIZE,RADAR_READ,TOA);
    return toa_list;
}

#endif